// @ts-check
/**
 * 计算排列 A(n,x)  传两个一样的相当于计算阶乘
 * @param {number} n
 * @param {number} x
 * @returns {number}
 */
function ordering(n, x) {
  if (!Number.isInteger(n) || !Number.isInteger(x) || n < 0 || x < 0 || x > n) {
    throw new Error('计算组合n必须是非负整数，x必须是0到n之间的整数');
  }
  if (x === 0) {
    return 1;
  }
  let res = 1;
  const last = n - x + 1;
  for (let i = n; i >= last; i--) {
    res *= i;
  }
  if (res > Number.MAX_SAFE_INTEGER) {
    throw new Error('数据计算超出范围');
  }
  return res;
}

/**
 * 计算组合 C(n,x)
 * @param {number} n
 * @param {number} x
 * @returns {number}
 */
function combination(n, x) {
  if (!Number.isInteger(n) || !Number.isInteger(x) || n < 0 || x < 0 || x > n) {
    throw new Error('计算组合n必须是非负整数，x必须是0到n之间的整数');
  }
  const x2 = n - x;
  if (x2 < x) {
    return combination(n, x2);
  }
  if (x === 0) {
    return 1;
  }
  return ordering(n, x) / ordering(x, x);
}

/**
 *
 * @param {number} n
 * @param {number} p
 * @returns {{ avg: number, details: { [k: string]: string }}}
 */
function probability(n, p) {
  if (!Number.isInteger(n) || n < 0) {
    throw new Error('n必须是非负整数');
  }
  if (typeof p !== 'number' || p < 0 || p > 1) {
    throw new Error('p必须是0到1之间的数字');
  }
  // 1要特殊处理
  if (p === 1) {
    return { avg: n, details: { [n]: '100%' } };
  }

  let last = 0;
  let lastRes = (1 - p) ** n;
  const arr = [lastRes];

  const k = p / (1 - p);

  for (let cur = 1; cur <= n; cur++) {
    const ck = (n - last) / (last + 1);

    const curRes = lastRes * ck * k;
    arr.push(curRes);

    last = cur;
    lastRes = curRes;
  }
  const avg = +arr.reduce((p, e, i) => p + e * i, 0).toFixed(2);
  const details = Object.fromEntries(arr.map((e, i) => [i, +(e * 100).toFixed(1) + '%']));
  return { avg, details };
}

console.log(probability(5, 0.6));
